package controller;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import controller.ControllerTime.TimeState;
import controller.trafficlights.LightPriorityComparer;
import controller.trafficlights.TrafficLight;
import simulator.enums.Direction;
import simulator.enums.LaneType;

public class CrossRoad implements Runnable
{
	private Thread _thread;
	private boolean _running;
	
	private Lane[] _lanes;
	private TimeState _timeState;
	private TrafficLightPriorityQueue _queue;
	private List<TrafficLight> _activeLights;
	
	public CrossRoad()
	{
		_running = false;
		_lanes = new Lane[4];
		_lanes[0] = new Lane(Direction.NORTH);
		_lanes[1] = new Lane(Direction.EAST);
		_lanes[2] = new Lane(Direction.SOUTH);
		_lanes[3] = new Lane(Direction.WEST);
		LightPriorityComparer comparer = new LightPriorityComparer();
		_queue = new TrafficLightPriorityQueue(10, comparer);
		_activeLights = new ArrayList<TrafficLight>();
	}
	
	public void loopEntered(Direction dir, LaneType type)
	{
		//System.out.println("Car entered loop: " + dir.toString() + " " + type.toString());
		TrafficLight light = _lanes[dir.getID()].getTrafficLight(type);
		light.loopEntered();
		if(!light.isActive())
		{
			addLightToQueue(light);
		}
	}
	
	public void loopLeft(Direction dir, LaneType type)
	{
		//System.out.println("Car left loop: " + dir.toString() + " " + type.toString());
		TrafficLight light = _lanes[dir.getID()].getTrafficLight(type);
		light.loopLeft();
	}
	
	private void addLightToQueue(TrafficLight light)
	{
		_queue.add(light);
	}
	
	public void start()
	{
		if(!_running)
		{
			_timeState = ControllerTime.getInstance().getTimeState();
			_thread = new Thread(this);
			_thread.start();
			_running = true;
		}
	}
	
	public void stop()
	{
		if(_running)
		{
			_running = false;
			try 
			{
				_thread.join();
			} 
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
		}
	}
	
	private void activateLight(TrafficLight light)
	{
		light.activate();
		_activeLights.add(light);
	}
	
	private void getLightsFromQueue()
	{
		TrafficLight light = _queue.remove();
		activateLight(light);
		
		Iterator<TrafficLight> it = _queue.iterator();
		List<TrafficLight> removeList = new ArrayList<TrafficLight>();
		while(it.hasNext())
		{
			TrafficLight nextLight = it.next();
			if(light.isCompatibleWith(nextLight)) 
			{
				activateLight(nextLight);
				removeList.add(nextLight);
			}
		}
		for(TrafficLight t : removeList)
		{
			_queue.remove(t);
		}
	}
	
	private void isTimeStateChanged()
	{
		if(_timeState != ControllerTime.getInstance().getTimeState())
		{
			_timeState = ControllerTime.getInstance().getTimeState();
			changeMode(_timeState);
		}
	}
	
	private void changeMode(TimeState state)
	{
		if(state == TimeState.NORMAL)
		{
			for(Lane l : _lanes)
			{
				l.normalMode();
			}
		}
		if(state == TimeState.NIGHT)
		{
			for(Lane l : _lanes)
			{
				_queue.clear();
				_activeLights.clear();
				l.nightMode();
			}
		}
		if(state == TimeState.MORNING)
		{
			for(Lane l : _lanes)
			{
				_queue.clear();
				_activeLights.clear();
				l.morningMode();
			}
		}
	}

	@Override
	public void run() 
	{
		long currentTime = System.currentTimeMillis();
		long prevTime = currentTime;
		long elapsedTime = 0;
		
		while(_running)
		{
			isTimeStateChanged();
			
			prevTime = currentTime;
			currentTime = System.currentTimeMillis();
			elapsedTime = (currentTime - prevTime) * ControllerTime.getInstance().getMultiplier();
			
			if(_timeState == TimeState.NORMAL)
			{	
				if(_queue.size() > 0)
				{
					//Update the priority of the lights if necessary.
					Iterator<TrafficLight> it = _queue.iterator();
					while(it.hasNext())
					{
						TrafficLight nextLight = it.next();
						nextLight.updatePriority(elapsedTime);
					}
					
					if(_activeLights.size() == 0)
					{
						getLightsFromQueue();
					}
				}
				for(int i = 0; i < _activeLights.size(); i++)
				{
					_activeLights.get(i).update(elapsedTime);
					if(!_activeLights.get(i).isActive())
					{
						//If the light is red it can be removed from the active list.
						TrafficLight light = _activeLights.remove(i);
						//If the light still has cars waiting, 
						//it needs to re enter the queue to become active again later.
						if(light.isOccupied()) addLightToQueue(light);
					}
				}
			}
			
			JSONSender.sendArray();
			
			try 
			{
				Thread.sleep(5);
			} 
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
		}
	}
}
